﻿using PredictionBuilder.Enum;
using PredictionBuilder.Model;
using PredictionBuilder.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace PredictionBuilder.Abstract
{
    /// <summary>
    /// 抽象基类
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    public abstract class AbstractBuilder<TModel> where TModel : class, new()
    {
        /// <summary>
        /// base ctor
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ignoreValues"></param>
        /// <param name="types"></param>
        public AbstractBuilder(TModel model, object[] ignoreValues, params Type[] types)
        {
            builderModel = new BuilderModel<TModel>(ignoreValues, types);
            this.model = model;
        }
        /// <summary>
        /// 构造模型
        /// </summary>
        protected BuilderModel<TModel> builderModel;
        /// <summary>
        /// 查询实体
        /// </summary>
        protected TModel model;
        /// <summary>
        /// 参数名字典
        /// </summary>
        protected Dictionary<string, Type> NameType;
        /// <summary>
        /// 聚合信息
        /// </summary>
        protected List<AggregateInfo> aggregateInfos = new List<AggregateInfo>();
        /// <summary>
        /// 参数字典
        /// </summary>
        protected Dictionary<Type, List<(ParameterExpression, int)>> TypeParameter = new Dictionary<Type, List<(ParameterExpression, int)>>();
        /// <summary>
        /// 添加聚合条件
        /// </summary>
        /// <param name="outerJoinType">外链接类型</param>
        /// <param name="selectors">条件</param>
        protected void AddAggregateCondition(ConnectType outerJoinType = ConnectType.And, params Expression<Func<TModel, object>>[] selectors)
        {
            AggregateInfo aggregateInfo = new AggregateInfo()
            {
                OuterJoinType = outerJoinType
            };
            foreach (Expression<Func<TModel, object>> selector in selectors)
            {
                string propName = ExpressionHelper.GetPropertyName(selector);
                foreach (PropertyBuildInfo propertyBuildInfo in builderModel.PropertyBuildInfos)
                {
                    if (propertyBuildInfo.ModelPropertyInfo.Name == propName)
                    {
                        propertyBuildInfo.IsAggregate = true;
                        aggregateInfo.AggregatePropertyBuildInfo.Add(propertyBuildInfo);
                    }
                }
            }
            aggregateInfos.Add(aggregateInfo);
        }
        /// <summary>
        /// 初始化参数字典
        /// </summary>
        protected void InitParmeterMap()
        {
            int index = 0;
            foreach (var keyValuePair in NameType)
            {
                ParameterExpression parameter = Expression.Parameter(keyValuePair.Value, keyValuePair.Key);
                if (!TypeParameter.TryGetValue(keyValuePair.Value, out List<(ParameterExpression, int)> parameterExpressions))
                {
                    TypeParameter.Add(keyValuePair.Value, new List<(ParameterExpression, int)>() { (parameter, index) });
                }
                else
                {
                    parameterExpressions.Add((parameter, index));
                }
                index++;
            }
        }
        /// <summary>
        /// 生成预测表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyBuildInfos">属性构建信息</param>
        /// <param name="joinFunc">条件关联方式</param>
        /// <returns></returns>
        protected Expression<T> CreatePredition<T>(IEnumerable<PropertyBuildInfo> propertyBuildInfos, Func<Expression<T>, Expression<T>, PropertyBuildInfo, Expression<T>> joinFunc) where T : Delegate
        {
            Expression<T> pred = null;
            foreach (PropertyBuildInfo info in propertyBuildInfos)
            {
                var propValue = info.ModelPropertyInfo.GetValue(model);
                foreach (var targetType in info.TargetType)
                {
                    if (!info.SkipBuildAttribute.CanSkip(propValue))
                    {
                        ParameterExpression parameterExpression = TypeParameter[targetType.TargetType].FirstOrDefault().Item1;
                        Expression<T> exp;
                        if (!info.IsCustom)
                        {
                            exp = ExpressionHelper.CreatePrediction<T>(parameterExpression, info.OperatorAttribute.OperatorType,
                            targetType.TargetPropertyName, info.ModelPropertyInfo.GetValue(model),
                            info.ParameterOrderAttribute.ParameterOrderType,
                            TypeParameter.Values.SelectMany(t => t).OrderBy(t => t.Item2).Select(t => t.Item1).ToArray());
                        }
                        else
                        {
                            exp = ExpressionHelper.CreatePrediction<T>(parameterExpression, info.CustomMethodAttribute, targetType.TargetPropertyName, info.ModelPropertyInfo.GetValue(model),
                            info.ParameterOrderAttribute.ParameterOrderType,
                            TypeParameter.Values.SelectMany(t => t).OrderBy(t => t.Item2).Select(t => t.Item1).ToArray());
                        }
                        pred = joinFunc.Invoke(pred, exp, info);
                    }
                }
            }
            return pred;
        }

        /// <summary>
        /// 创建聚合表达式并和外界表达式进行拼接
        /// </summary>
        /// <typeparam name="T">委托类型</typeparam>
        /// <param name="orginExpression">源表达式</param>
        /// <param name="aggregateInfos">聚合信息</param>
        /// <returns>完整的表达式</returns>
        protected Expression<T> CreateAggregatePredition<T>(Expression<T> orginExpression, List<AggregateInfo> aggregateInfos) where T : Delegate
        {
            foreach (AggregateInfo aggregateInfo in aggregateInfos)
            {
                Expression<T> expression = null;
                expression = CreatePredition<T>(aggregateInfo.AggregatePropertyBuildInfo,
                    (orgin, exp, info) => ExpressionHelper.JoinExpression(orgin, exp, info.ConnectAttribute.ConnectType));
                orginExpression = ExpressionHelper.JoinExpression(orginExpression, expression, aggregateInfo.OuterJoinType);
            }
            return orginExpression;
        }

    }
}
